﻿using LogHelper;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;

namespace HttpClientHelper
{
    public class HttpClients : IHttpClients
    {
        private readonly IHttpClientFactory _httpClientFactory;
        private readonly ILogServices _logServices;

        public HttpClients(IHttpClientFactory httpClientFactory, ILogServices logServices)
        {
            this._httpClientFactory = httpClientFactory;
            this._logServices = logServices;
        }

        /// <summary>
        /// Get
        /// </summary>
        /// <param name="url"></param>
        /// <param name="dicHeaders"></param>
        /// <param name="timeoutSecond"></param>
        /// <returns></returns>
        public async Task<T?> GetAsync<T>(string url, Dictionary<string, string> dicHeaders, int timeoutSecond = 180)
        {
            try
            {
                dicHeaders = new Dictionary<string, string>();
                dicHeaders.Add("interior", "interior");
                var client = BuildHttpClient(dicHeaders, timeoutSecond);
                var response = await client.GetAsync(url);
                var responseContent = await response.Content.ReadAsStringAsync();
                if (response.IsSuccessStatusCode)
                {

                    return JsonConvert.DeserializeObject<T>(responseContent);
                }
                else
                {
                    _logServices.Log(response.StatusCode.ToString() + responseContent);
                    return default;
                }
            }
            catch (Exception ex)
            {
                _logServices.Log($"HttpGet:{url} Error" + ex.Message);
                return default;
            }
        }
        /// <summary>
        /// Post
        /// </summary>
        /// <param name="url"></param>
        /// <param name="requestBody"></param>
        /// <param name="dicHeaders"></param>
        /// <param name="timeoutSecond"></param>
        /// <returns></returns>
        public async Task<T?> PostAsync<T>(string url, string requestBody, Dictionary<string, string> dicHeaders, int timeoutSecond = 180)
        {
            try
            {
                dicHeaders = new Dictionary<string, string>();
                dicHeaders.Add("interior", "interior");
                var client = BuildHttpClient(dicHeaders, timeoutSecond);
                var requestContent = GenerateStringContent(requestBody, dicHeaders);
                var response = await client.PostAsync(url, requestContent);
                var responseContent = await response.Content.ReadAsStringAsync();
                if (response.IsSuccessStatusCode)
                {
                    var result = JsonConvert.DeserializeObject<T>(responseContent);
                    return result;
                }
                else
                {
                    _logServices.Log(response.StatusCode.ToString() + responseContent);
                    return default;
                }
            }
            catch (Exception ex)
            {
                _logServices.Log($"HttpPost:{url} Error" + ex.Message);
                return default;
            }
        }
        /// <summary>
        /// Put
        /// </summary>
        /// <param name="url"></param>
        /// <param name="requestBody"></param>
        /// <param name="dicHeaders"></param>
        /// <param name="timeoutSecond"></param>
        /// <returns></returns>
        public async Task<T> PutAsync<T>(string url, string requestBody, Dictionary<string, string> dicHeaders, int timeoutSecond = 180)
        {
            try
            {
                var client = BuildHttpClient(null, timeoutSecond);
                var requestContent = GenerateStringContent(requestBody, dicHeaders);
                var response = await client.PutAsync(url, requestContent);
                var responseContent = await response.Content.ReadAsStringAsync();
                if (response.IsSuccessStatusCode)
                {
                    var result = JsonConvert.DeserializeObject<T>(responseContent);
                    return result;
                }
                else
                {
                    throw new CustomerHttpException(response.StatusCode.ToString(), responseContent);
                }
            }
            catch (Exception ex)
            {
                throw new Exception($"HttpPut:{url} Error", ex);
            }
        }

        /// <summary>
        /// Patch
        /// </summary>
        /// <param name="url"></param>
        /// <param name="requestString"></param>
        /// <param name="dicHeaders"></param>
        /// <param name="timeoutSecond"></param>
        /// <returns></returns>
        public async Task<T> PatchAsync<T>(string url, string requestBody, Dictionary<string, string> dicHeaders, int timeoutSecond = 180)
        {
            try
            {
                var client = BuildHttpClient(null, timeoutSecond);
                var requestContent = GenerateStringContent(requestBody, dicHeaders);
                var response = await client.PatchAsync(url, requestContent);
                var responseContent = await response.Content.ReadAsStringAsync();
                if (response.IsSuccessStatusCode)
                {
                    var result = JsonConvert.DeserializeObject<T>(responseContent);
                    return result;
                }
                else
                {
                    throw new CustomerHttpException(response.StatusCode.ToString(), responseContent);
                }
            }
            catch (Exception ex)
            {
                throw new Exception($"HttpPatch:{url} Error", ex);
            }
        }
        /// <summary>
        /// Delete
        /// </summary>
        /// <param name="url"></param>
        /// <param name="dicHeaders"></param>
        /// <param name="timeoutSecond"></param>
        /// <returns></returns>
        public async Task<T> DeleteAsync<T>(string url, Dictionary<string, string> dicHeaders, int timeoutSecond = 180)
        {
            try
            {
                var client = BuildHttpClient(dicHeaders, timeoutSecond);
                var response = await client.DeleteAsync(url);
                var responseContent = await response.Content.ReadAsStringAsync();
                if (response.IsSuccessStatusCode)
                {
                    var result = JsonConvert.DeserializeObject<T>(responseContent);
                    return result;
                }
                else
                {
                    throw new CustomerHttpException(response.StatusCode.ToString(), responseContent);
                }
            }
            catch (Exception ex)
            {
                throw new Exception($"HttpDelete:{url} Error", ex);
            }
        }
        /// <summary>
        /// common request
        /// </summary>
        /// <param name="url"></param>
        /// <param name="method"></param>
        /// <param name="requestBody"></param>
        /// <param name="dicHeaders"></param>
        /// <param name="timeoutSecond"></param>
        /// <returns></returns>
        public async Task<T> ExecuteAsync<T>(string url, HttpMethod method, string requestBody, Dictionary<string, string> dicHeaders, int timeoutSecond = 180)
        {
            try
            {
                var client = BuildHttpClient(null, timeoutSecond);
                var request = GenerateHttpRequestMessage(url, requestBody, method, dicHeaders);
                var response = await client.SendAsync(request);
                var responseContent = await response.Content.ReadAsStringAsync();
                if (response.IsSuccessStatusCode)
                {
                    var type = typeof(T);
                    if (type.IsPrimitive || type == typeof(string))
                    {
                        return (T)Convert.ChangeType(responseContent, typeof(T));
                    }
                    else
                    {
                        return JsonConvert.DeserializeObject<T>(responseContent);
                    }
                }
                else
                {
                    throw new CustomerHttpException(response.StatusCode.ToString(), responseContent);
                }
            }
            catch (Exception ex)
            {
                throw new Exception($"{method.ToString()}:{url} Error", ex);
            }

        }
        /// <summary>
        /// Build HttpClient
        /// </summary>
        /// <param name="timeoutSecond"></param>
        /// <returns></returns>
        private HttpClient BuildHttpClient(Dictionary<string, string> dicDefaultHeaders, int? timeoutSecond)
        {
            var httpClient = _httpClientFactory.CreateClient();
            httpClient.DefaultRequestHeaders.Clear();   //in order that the client is not affected by the last request,it need to clear DefaultRequestHeaders
            httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            if (dicDefaultHeaders != null)
            {
                foreach (var headerItem in dicDefaultHeaders)
                {
                    if (!httpClient.DefaultRequestHeaders.Contains(headerItem.Key))
                    {
                        httpClient.DefaultRequestHeaders.Add(headerItem.Key, headerItem.Value);
                    }
                }
            }
            if (timeoutSecond != (int?)null)
            {
                httpClient.Timeout = TimeSpan.FromSeconds(timeoutSecond.Value);
            }
            return httpClient;
        }

        /// <summary>
        /// Generate HttpRequestMessage
        /// </summary>
        /// <param name="url"></param>
        /// <param name="requestBody"></param>
        /// <param name="method"></param>
        /// <param name="dicHeaders"></param>
        /// <returns></returns>
        private HttpRequestMessage GenerateHttpRequestMessage(string url, string requestBody, HttpMethod method, Dictionary<string, string> dicHeaders)
        {
            var request = new HttpRequestMessage(method, url);
            if (!string.IsNullOrEmpty(requestBody))
            {
                request.Content = new StringContent(requestBody);
            }
            if (dicHeaders != null)
            {
                foreach (var header in dicHeaders)
                {
                    request.Headers.Add(header.Key, header.Value);
                }
            }
            return request;
        }
        /// <summary>
        ///  Generate StringContent
        /// </summary>
        /// <param name="requestBody"></param>
        /// <param name="dicHeaders"></param>
        /// <returns></returns>
        private StringContent GenerateStringContent(string requestBody, Dictionary<string, string> dicHeaders)
        {
            var content = new StringContent(requestBody);
            if (dicHeaders != null)
            {
                foreach (var headerItem in dicHeaders)
                {
                    content.Headers.Add(headerItem.Key, headerItem.Value);
                }
            }
            return content;
        }
    }
}
